/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <mc/exception.h>
#include <mc/filesystem.h>
#include <mc/log.h>
#include <mc/string.h>
#include <mc/format.h>

#include "app_schema.h"

namespace devmon {

// 这里需要通过dbus获取customer name
void app_schema::init(std::unordered_map<std::string, std::string>& app_paths) {
    m_classes.clear();
    for (const auto& [name, path] : app_paths) {
        load({path});
    }
}

void app_schema::load(std::vector<std::string> paths) {
    for (const auto& path : paths) {
        if (!mc::filesystem::exists(path)) {
            wlog("App schema path not exists: ${path}", ("path", path));
            continue;
        }

        if (!mc::filesystem::is_directory(path)) {
            wlog("App schema path is not a directory: ${path}", ("path", path));
            continue;
        }

        auto apps = mc::filesystem::list_directories(path);
        for (const auto& name : apps) {
            std::string file_path = (name / "mds" / "schema.json").string();
            if (!mc::filesystem::exists(file_path)) {
                continue;
            }
            if (!mc::filesystem::is_regular_file(file_path)) {
                wlog("App schema file is not a regular file: ${path}", ("path", file_path));
                continue;
            }
            try {
                auto schema_str = mc::filesystem::read_file(file_path);
                if (!schema_str.has_value()) {
                    wlog("Failed to read schema file: ${path}", ("path", file_path));
                    continue;
                }
                auto     schema      = mc::json::json_decode(*schema_str);
                mc::dict schema_dict = schema.as<mc::dict>();

                // 解析schema
                std::string app_name = name.filename().string();
                dlog("get app: ${name} schema: ${path}", ("name", app_name)("path", file_path));
                parse_schema(app_name, schema_dict);
            } catch (const std::exception& e) {
                wlog("Failed to parse schema file: ${path}, error: ${error}",
                     ("path", file_path)("error", e.what()));
            }
        }
    }
}

void app_schema::parse_schema(std::string app, mc::dict& schema) {
    // 获取defs节点，包含所有类定义
    if (!schema.contains("defs") || !schema["defs"].is_object()) {
        wlog("Schema does not contain 'defs' section, app: ${app}", ("app", app));
        return;
    }

    auto defs = schema["defs"].as<mc::dict>();

    // 遍历所有类定义
    for (auto it = defs.begin(); it != defs.end(); ++it) {
        const std::string& class_name = it->key.as<std::string>();
        const auto&        class_data = it->value.as<mc::dict>();

        if (m_classes.find(class_name) != m_classes.end()) {
            wlog("Class ${name} already exists", ("name", class_name));
            continue;
        }

        class_info cls;
        cls.app  = app;
        cls.name = class_name;

        // 解析路径
        if (class_data.contains("path") && class_data["path"].is_string()) {
            cls.path = class_data["path"].as_string();
        }

        // 解析属性
        if (class_data.contains("properties") && class_data["properties"].is_object()) {
            auto properties = class_data["properties"].as<mc::dict>();
            for (auto it = properties.begin(); it != properties.end(); ++it) {
                const std::string& prop_name = it->key.as<std::string>();
                const auto&        prop_data = it->value.as<mc::dict>();
                auto               prop_info = parse_property(prop_name, prop_data);
                cls.properties[prop_name]    = prop_info;
            }
        }

        // 解析接口
        if (class_data.contains("interfaces") && class_data["interfaces"].is_object()) {
            auto interfaces = class_data["interfaces"].as<mc::dict>();
            for (auto it = interfaces.begin(); it != interfaces.end(); ++it) {
                const std::string& interface_name  = it->key.as<std::string>();
                const auto&        interface_props = it->value.as_array();

                for (const auto& prop : interface_props) {
                    if (prop.is_string()) {
                        cls.properties[prop.as_string()].interface = interface_name;
                    }
                }

                cls.interfaces.push_back(interface_name);
            }
        }

        m_classes[cls.name] = cls;
        dlog("Parsed class: ${name}, properties: ${prop_count}, interfaces: ${iface_count}, path: "
             "${path}",
             ("name", cls.name)("prop_count", cls.properties.size())(
                 "iface_count", cls.interfaces.size())("path", cls.path));
    }
}

std::string app_schema::extract_type_from_ref(const std::string& ref) {
    // 从 "base.json#/String" 中提取 "String"
    auto hash_pos = ref.find('#');
    if (hash_pos != std::string::npos && hash_pos + 2 < ref.length()) {
        return ref.substr(hash_pos + 2); // 跳过 "#/"
    }
    return ref;
}

property_info app_schema::parse_property(const std::string& prop_name, const mc::dict& prop_data) {
    property_info prop;
    prop.name = prop_name;

    // 解析allOf数组
    if (prop_data.contains("allOf") && prop_data["allOf"].is_array()) {
        auto all_of = prop_data["allOf"].as_array();

        for (const auto& item : all_of) {
            if (!item.is_object()) {
                continue;
            }

            // 查找$ref来获取类型
            if (item.contains("$ref") && item["$ref"].is_string()) {
                prop.type = extract_type_from_ref(item["$ref"].as_string());
            }

            // 查找title来获取origin name
            if (item.contains("title") && item["title"].is_string()) {
                prop.origin_name = item["title"].as_string();
            }
        }
    }

    // // 如果没有找到origin name，使用属性名作为默认值
    // if (prop.origin_name.empty()) {
    //     prop.origin_name = prop_name;
    // }

    return prop;
}

class_base_info app_schema::get_class_info(const std::string& class_name, mc::dict& ctx) {
    if (m_classes.find(class_name) == m_classes.end()) {
        wlog("Class ${name} not found", ("name", class_name));
        return class_base_info();
    }

    class_base_info base_info;

    base_info.app  = m_classes[class_name].app;
    base_info.path = mc::format_dict_icase(m_classes[class_name].path, ctx);
    return base_info;
}

std::string app_schema::get_property_intf(const std::string& class_name,
                                          const std::string& property_name) {
    if (m_classes.find(class_name) == m_classes.end()) {
        wlog("Class ${name} not found", ("name", class_name));
        return "";
    }

    if (m_classes[class_name].properties.find(property_name) ==
        m_classes[class_name].properties.end()) {
        wlog("Property ${name} not found in class ${class_name}",
             ("name", property_name)("class_name", class_name));
        return "";
    }

    return m_classes[class_name].properties[property_name].interface;
}

std::string app_schema::get_property_type(const std::string& class_name,
                                          const std::string& property_name) {
    if (class_name == "Func") {
        return "";
    }

    if (m_classes.find(class_name) == m_classes.end()) {
        wlog("Class ${name} not found", ("name", class_name));
        return "";
    }

    if (m_classes[class_name].properties.find(property_name) ==
        m_classes[class_name].properties.end()) {
        wlog("Property ${name} not found in class ${class_name}",
             ("name", property_name)("class_name", class_name));
        return "";
    }

    return m_classes[class_name].properties[property_name].type;
}

std::string app_schema::get_property_origin_name(const std::string& class_name,
                                                 const std::string& property_name) {
    if (m_classes.find(class_name) == m_classes.end()) {
        wlog("Class ${name} not found", ("name", class_name));
        return "";
    }

    if (m_classes[class_name].properties.find(property_name) ==
        m_classes[class_name].properties.end()) {
        wlog("Property ${name} not found in class ${class_name}",
             ("name", property_name)("class_name", class_name));
        return "";
    }

    return m_classes[class_name].properties[property_name].origin_name;
}
} // namespace devmon
